/*
 * Copyright (c) 2014 by Hell Prototypes <www.hellprototypes.com>
 * Attiny24A + nRF24L01 board main code
 *
 * This file is free software; you can redistribute it and/or modify
 * it under the terms of either the GNU General Public License version 2
 * or the GNU Lesser General Public License version 2.1, both as
 * published by the Free Software Foundation.
 */

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/eeprom.h>
#include <MirfHardwareSpiDriver85.h>
#include <Mirf85.h>
#include <SPI85.h>

#define INT0_LOW_LEVEL_MODE()		{ MCUCR &= ~(_BV(ISC01)|_BV(ISC00)); }
#define INT0_FALLING_EDGE_MODE()	{ MCUCR = (MCUCR & ~(_BV(ISC01)|_BV(ISC00))) | _BV(ISC01); }
#define INT0_INT_ENABLE()			{ /*GIFR = _BV(INTF0);*/ GIMSK |=  _BV(INT0); }
#define INT0_INT_DISABLE()		{ GIMSK &= ~_BV(INT0); }

#define PCINT10_ENABLE()			{ GIMSK |=  _BV(PCIE1); PCMSK1 |=  _BV(PCINT10); }
#define PCINT10_DISABLE()			{ GIMSK &= ~_BV(PCIE1); PCMSK1 &= ~_BV(PCINT10); }

#define PCINT7_ENABLE()				{ GIMSK |=  _BV(PCIE0); PCMSK0 |=  _BV(PCINT7); }
#define PCINT7_DISABLE()			{ GIMSK &= ~_BV(PCIE0); PCMSK0 &= ~_BV(PCINT7); }

#define TIMER0_MATCH_VALUE	(155)//64*156 ~= 10ms
#define TIMER0_START()			{ TCCR0B = _BV(CS01)|_BV(CS00);} //clkI/O/64
#define TIMER0_STOP()			{ TCCR0B = 0x00;} //clkI/O/256
#define TIMER0_RESET()			{ TCNT0 = 0; TIFR0 = _BV(OCIE0A); }
#define WAIT_1_ETU()			{ while( !(TIFR0 & _BV(OCF0A))); TIFR0 = _BV(OCIE0A); }

#define ms_delay(ms)			delay10ms(ms/10)

#define PIN_LOW(port, pin)		(PORT##port &= ~_BV(pin))
#define PIN_HIGH(port, pin)	(PORT##port |= _BV(pin))
#define PIN_INPUT(port, pin)	(DDR##port  &= ~_BV(pin))
#define PIN_OUTPUT(port, pin)	(DDR##port  |= _BV(pin))
#define PIN_GET(port, pin)		(PIN##port  & _BV(pin))
#define PIN_TOGGLE(port, pin)	(PIN##port = _BV(pin))

#define PIN_KEY					1 //PB1
#define PIN_MAG_DET			2 //PB2/INT0
#define PIN_AIN_EN				0 //PB0

#define PIN_VIBRATE				7// PA7
//---------------------------------------------------------------------
#define ADC_STANDBY()			{ADCSRA = (1 << ADEN) | (1 << ADIF);}
#define ADC_START()			{ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADIF);}
#define ADC_STOP()				{ADCSRA = (1 << ADIF);}

//---------------------------------------------------------------------
typedef union {
	uint8_t bufferout[10];//8byte
	struct {
		uint32_t current;
		uint32_t total;
		uint16_t voltage;
	};
} u_WL_Data;

u_WL_Data g_WL_Data;

//---------------------------------------------------------------------
uint16_t g_input_count;
uint8_t g_timer0_cnt;
//---------------------------------------------------------------------
void ADC_init(void)
{
	PIN_HIGH(B,PIN_AIN_EN);
	ADC_STANDBY();
	ADMUX = (1 << REFS1) | (1 << MUX0);//1.1V ref, ADC1
}

void ADC_deinit(void)
{
	ADMUX = (1 << MUX0);
	ADC_STOP();
	PIN_LOW(B,PIN_AIN_EN);
}

uint16_t ADC_single_convert(void)
{
	uint16_t ad_value;

	ADC_init();

	//The first ADC conversion result after switching reference voltage source may be inaccurate, and
	//the user is advised to discard this result.
	ADC_START();
	while(!(ADCSRA & _BV(ADIF)));
	
	ADC_START();
	while(!(ADCSRA & _BV(ADIF)));
	ad_value = ADCW;
	ADC_deinit();

	return ad_value;
}

//---------------------------------------------------------------------

ISR(TIM0_COMPA_vect) {
	g_timer0_cnt--;
}

void delay10ms(uint8_t cnt) {
	g_timer0_cnt = cnt;
	TIMER0_RESET();
	TIMER0_START();
	while(g_timer0_cnt) {
		sleep_mode();
	}
	TIMER0_STOP();
}


void setup() {
	OCR0A = TIMER0_MATCH_VALUE;
	TCCR0A = _BV(WGM01);	//CTC Mode
	TIMSK0 = _BV(OCIE0A); //enable intrrupte

	PIN_LOW(B,PIN_AIN_EN);
	PIN_OUTPUT(B,PIN_AIN_EN);

	//PIN_INPUT(A, PIN_VIBRATE); //reset default
	//PIN_HIGH(A, PIN_VIBRATE);//pull up enable

	//PIN_INPUT(B,PIN_MAG_DET); //reset default
	PIN_HIGH(B, PIN_MAG_DET);//pull up enable
	INT0_INT_ENABLE();

	PRR = _BV(PRTIM1);//shutdown timer1
	ACSR = (1 << ACD);//Switched off Analog Comparator
	DIDR0 = 1<< ADC1D;//PA1 digital input buffer disable

	//set_sleep_mode(SLEEP_MODE_IDLE); //default mode was IDLE
	sleep_enable();
	sei();

	ms_delay(200);//wait for nrf24 power on reset

	//------------------------------
	Mirf.spi = &MirfHardwareSpi;
	Mirf.init();

	//uint8_t RADDR[] = {0xe2, 0xf0, 0xf0, 0xf0, 0xf0};
	//uint8_t TADDR[] = {0xe7, 0xe7, 0xe7, 0xe7, 0xe7};

	// Get nodeID from TXADDR and mask the first byte (0xff)

	//Mirf.setRADDR(RADDR);  
	//Mirf.setTADDR(TADDR);//use default tx address
	Mirf.config();

	// Enable dynamic payload 
	//Mirf.configRegister( FEATURE, 1<<EN_DPL ); 
	//Mirf.configRegister( DYNPD, 1<<DPL_P0 | 1<<DPL_P1 | 1<<DPL_P2 | 1<<DPL_P3 | 1<<DPL_P4 | 1<<DPL_P5 ); 
}

void send_package(void) {
	Mirf.powerUpTx();
	ms_delay(10);
	Mirf.send((uint8_t *) g_WL_Data.bufferout);
	while( Mirf.isSending() );
	Mirf.powerDown();
}

int main(void)
{
	uint16_t second_cnt, last_input_count;

	setup();

	for(uint8_t i=0; i<10; i++) {
		g_WL_Data.bufferout[i] = i;
	}
	g_WL_Data.current = 10;
	g_WL_Data.total = 1000;
	g_input_count = 0;
	last_input_count = 0;

	while(1) {
		second_cnt = 0;
		set_sleep_mode(SLEEP_MODE_IDLE);
		INT0_FALLING_EDGE_MODE();

		while(1) {
			if(g_input_count != last_input_count) {
				g_WL_Data.current += g_input_count - last_input_count;
				g_WL_Data.total   += g_input_count - last_input_count;
				g_WL_Data.voltage = ADC_single_convert();
				last_input_count    = g_input_count;
				send_package();
				second_cnt = 0;
			} else {
				ms_delay(20);
				if(second_cnt++ > 10) {//5min
					break;
				}
			}
			ms_delay(980);
		}

		INT0_LOW_LEVEL_MODE();
		set_sleep_mode(SLEEP_MODE_PWR_DOWN);
		sleep_mode();
	}
}

ISR(EXT_INT0_vect) {
	g_input_count++;
}

//EMPTY_INTERRUPT(PCINT1_vect);//EMPTY_INTERRUPT(PCINT0_vect);
